package scuff.msg.json.jackson

import scuff.DoubleDispatch
import scuff.msg._
import scuff.msg.util._
import scuff.msg.json._
import scuff.msg.json.jackson._

import org.junit._
import org.junit.Assert._

class TestJackson {

  implicit var out: java.io.StringWriter = _
  var reader: ReflectiveJacksonReader[TestCallback, TestMessage] = _
  var writer: ReflectiveJacksonWriter[TestMessage] = _
  implicit val callback = new TestCallback {
    var lastBid, lastAsk = Float.NaN
    var lastTicker: String = _
    var lastHalted = false
    var isShutdown = false
    def onMessage(msg: StockQuote) {
      lastBid = msg.bid
      lastAsk = msg.ask
      lastTicker = msg.ticker
    }
    def onMessage(msg: StockStatus) {
      lastHalted = msg.halted
      lastTicker = msg.ticker
    }
    def onMessage(msg: Shutdown) = isShutdown = true
  }

  @Before
  def setup() {
    out = new java.io.StringWriter
    reader = new ReflectiveJacksonReader[TestCallback, TestMessage] with IdAsClassName[TestMessage] {
      val basePackage = getClass.getPackage
      def callback = TestJackson.this.callback
    }
    writer = new ReflectiveJacksonWriter[TestMessage] with ClassNameAsType
  }

  @Test
  def marshalHallow() {
    out.write("Jackson: ")
    writer.write(new Shutdown)
    println(out)
  }

  @Test
  def marshal() {
    val quote = new StockQuote
    quote.ticker = "MSFT"
    quote.bid = 23.21f
    quote.ask = 23.24f
    out.write("Jackson: ")
    writer.write(quote)
    println(out)
  }

  @Test
  def quote() {
    val json = """
{
      "id": "StockQuote",
      "payload": {
          "ticker": "MSFT",
          "ask": 42.51,
          "bid": 42.48
      }
}
"""
    assertTrue(callback.lastAsk.isNaN)
    assertTrue(callback.lastBid.isNaN)
    assertNull(callback.lastTicker)
    reader.read(json)
    assertEquals("MSFT", callback.lastTicker)
    assertEquals(42.51f, callback.lastAsk, 0.0f)
    assertEquals(42.48f, callback.lastBid, 0.0f)
  }

  @Test
  def halted() {
    val json = """
{
      "id": "StockStatus",
      "payload": {
          "ticker": "SIRI",
          "halted": true
      }
}
"""
    assertFalse(callback.lastHalted)
    assertNull(callback.lastTicker)
    reader.read(json)
    assertEquals("SIRI", callback.lastTicker)
    assertTrue(callback.lastHalted)
  }

  @Test
  def noPayload() {
    val json = "{ \"id\": \"Shutdown\" }"
    assertFalse(callback.isShutdown)
    reader.read(json)
    assertTrue(callback.isShutdown)
  }
}

trait TestCallback {
  def onMessage(msg: StockQuote)
  def onMessage(msg: StockStatus)
  def onMessage(msg: Shutdown)
}

import scala.reflect._

abstract class TestMessage extends DoubleDispatch[TestCallback] {
  @transient
  val id = getClass.getSimpleName
}

class StockQuote extends TestMessage {
  @BeanProperty
  var ticker: String = _
  @BeanProperty
  var bid: Float = _
  @BeanProperty
  var ask: Float = _
  def dispatch(cb: TestCallback) = cb.onMessage(this)
}

class Shutdown extends TestMessage {
  def dispatch(cb: TestCallback) = cb.onMessage(this)
}

class StockStatus extends TestMessage {
  @BeanProperty
  var ticker: String = _
  @BeanProperty
  var halted: Boolean = _
  def dispatch(cb: TestCallback) = cb.onMessage(this)
}
